package com.sailing.xjpb.consul;

import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.QueryParams;
import com.ecwid.consul.v1.Response;
import com.ecwid.consul.v1.agent.model.NewService;
import com.ecwid.consul.v1.health.HealthServicesRequest;
import com.ecwid.consul.v1.health.model.HealthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * consul服务重新注册
 *
 * @author 罗志银 createDate 2020/8/10 17:51
 */
@Slf4j
public class ConsulRetryRegistry implements CommandLineRunner {

    private final ConsulClient client;
    private ScheduledExecutorService pools = Executors.newSingleThreadScheduledExecutor();
    private ConsulAutoRegistration consulAutoRegistration;
    private ConsulServiceRegistry consulServiceRegistry;
    private ConsulRetryProperties properties;
    private ConsulDiscoveryProperties consulDiscoveryProperties;

    public ConsulRetryRegistry(ConsulDiscoveryProperties consulDiscoveryProperties,
                               ConsulAutoRegistration consulAutoRegistration,
                               ConsulServiceRegistry consulServiceRegistry, ConsulRetryProperties properties, ConsulClient client) {
        this.consulDiscoveryProperties = consulDiscoveryProperties;
        this.consulAutoRegistration = consulAutoRegistration;
        this.consulServiceRegistry = consulServiceRegistry;
        this.properties = properties;
        this.client = client;
    }

    @Override
    public void run(String... args) {
        // 获取当前服务
        final NewService service = this.consulAutoRegistration.getService();
        final String serviceName = service.getName();
        final String serviceId = service.getId();
        // 启动一个线程进行服务监测
        pools.scheduleAtFixedRate(() -> {
            if (log.isDebugEnabled()) {
                log.debug("consul服务监测已启动【{}】", serviceName);
            }
            // 健康检查
            if (!this.checkStatusForService(serviceName, serviceId)) {
                try {
                    // 重新注册
                    this.registry();
                    log.info("consul服务重新注册成功【{}】", serviceName);
                } catch (Exception e) {
                    log.warn("consul服务当前注册失败，准备下一次注册【{}】", serviceName);
                }
            }
        }, 0, this.properties.getInitialInterval(), TimeUnit.MILLISECONDS);
    }

    /**
     * 服务注册
     */
    private void registry() {
        this.consulServiceRegistry.register(this.consulAutoRegistration);
    }

    /**
     * 检测服务是否正常 ，此方法必须在保证服务启动健康检查能通过的情况下。如果健康检查URL 都不对那肯定是不正常的。
     *
     * @param serviceId  服务名
     * @param instanceId 唯一标识
     * @return
     */
    private boolean checkStatusForService(String serviceId, String instanceId) {
        try {
            HealthServicesRequest healthServicesRequest = HealthServicesRequest.newBuilder()
                    .setQueryParams(QueryParams.DEFAULT).setPassing(false)
                    .setToken(consulDiscoveryProperties.getAclToken()).build();
            Response<List<HealthService>> response = client.getHealthServices(serviceId, healthServicesRequest);
            List<HealthService> healthServiceList = response.getValue();
            return healthServiceList.stream()
                    .anyMatch(healthService -> instanceId.equals(healthService.getService().getId()));
        } catch (Exception e) {
            log.debug("consul服务心跳检测结束，检测异常：{}", e.getMessage());
            return true;
        }
    }
}
